package jp.sourceforge.talisman.mds.distance;

/*
 * $Id: DistanceCalculatorFactory.java,v 1.1 2009/09/12 05:06:08 weiwei Exp $
 */

import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author Haruaki Tamada
 * @version $Revision: 1.1 $ 
 */
public class DistanceCalculatorFactory{
    private static final DistanceCalculatorFactory factory = new DistanceCalculatorFactory();

    private Map<ItemDistanceAlgorithm, ItemDistanceCalculator> itemMap = new HashMap<ItemDistanceAlgorithm, ItemDistanceCalculator>();
    private Map<ClusterDistanceAlgorithm, Class<? extends ClusterDistanceCalculator>> clusterMap = 
        new HashMap<ClusterDistanceAlgorithm, Class<? extends ClusterDistanceCalculator>>();

    private DistanceCalculatorFactory(){
        itemMap.put(ItemDistanceAlgorithm.CITY_BLOCK_DISTANCE, new CityBlockItemDistanceCalculator());
        itemMap.put(ItemDistanceAlgorithm.DOMINANCE_METRIC,    new DominanceMetricCalculator());
        itemMap.put(ItemDistanceAlgorithm.NORMALIZED_EUCLIDEAN_DISTANCE, new NormalizedEuclideanItemDistanceCalculator());
        itemMap.put(ItemDistanceAlgorithm.SQUARED_EUCLIDEAN_DISTANCE, new SquaredEuclideanItemDistanceCalculator());

        clusterMap.put(ClusterDistanceAlgorithm.CENTROID_METHOD, CentroidClusterDistanceCalculator.class);
        clusterMap.put(ClusterDistanceAlgorithm.FURTHEST_NEIGHBOR_METHOD, FurthestNeighborClusterDistanceCalculator.class);
        clusterMap.put(ClusterDistanceAlgorithm.NEAREST_NEIGHBOR_METHOD, NearestNeighborClusterDistanceCalculator.class);
        clusterMap.put(ClusterDistanceAlgorithm.GROUP_AVERAGE_METHOD, GroupAverageClusterDistanceCalculator.class);
        clusterMap.put(ClusterDistanceAlgorithm.WARDS_METHOD, WardsClusterDistanceCalculator.class);
    }

    public static DistanceCalculatorFactory getInstance(){
        return factory;
    }

    public synchronized ClusterDistanceCalculator getClusterDistanceCalculator(ClusterDistanceAlgorithm algorithm,
            ItemDistanceAlgorithm itemAlgorithm) throws DistanceCalculatorNotFoundException{
        try{
            Class<? extends ClusterDistanceCalculator> clazz = clusterMap.get(algorithm);
            ClusterDistanceCalculator cd = clazz.newInstance();
            if(itemAlgorithm == null){
                throw new DistanceCalculatorNotFoundException("item distance algorithm is null");
            }
            cd.setItemDistanceCalculator(itemAlgorithm);

            return cd;
        } catch(DistanceCalculatorNotFoundException e){
            throw e;
        } catch(Exception e){
            throw new DistanceCalculatorNotFoundException(algorithm + " not found");
        }
    }

    public synchronized ItemDistanceCalculator getItemDistanceCalculator(ItemDistanceAlgorithm distance){
        ItemDistanceCalculator dc;
        if(distance == ItemDistanceAlgorithm.MINKOWSKY_DISTANCE){
            // This class has attribute p-value.
            // Therefore, this class is always constructed for multi-thread.
            dc = new MinkowskyItemDistanceCalculator();
        }
        else{
            dc = itemMap.get(distance);
        }
        return dc;
    }

    public ItemDistanceCalculator getDefaultItemDistanceCalculator(){
        return getItemDistanceCalculator(ItemDistanceAlgorithm.SQUARED_EUCLIDEAN_DISTANCE);
    }
}
